﻿using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using MbUnit.Framework;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using QuailServices.QuailDataServiceReference;

namespace Quail
{
    /// <summary>
    /// The network traffic information from a call to Selenium's CaptureNetworkTraffic
    /// </summary>
    [TestFixture]
    public class NetworkTraffic
    {
        private readonly WebBrowser _browser;
        private readonly Dictionary<string, int> _urlCounters = new Dictionary<string, int>();

        /// <summary>
        /// Initializes a new instance of the <see cref="NetworkTraffic"/> class.
        /// </summary>
        /// <param name="browser">The browser.</param>
        public NetworkTraffic(WebBrowser browser)
        {
            _browser = browser;
        }

        [Test]
        private void Test()
        {
            string json;

            using (var r = new StreamReader("c:\\work\\json2.txt"))
            {
                json = r.ReadToEnd();
            }

            var result = DeserializeNetworkJson(json);

        	Assert.IsNotNull(result);
        }

    	/// <summary>
        /// Gets the list traffic from the last web page
        /// </summary>
        /// <returns>the list of traffic since the last call</returns>
        public List<NetworkTrafficData> GetTraffic()
        {
            var task = _browser.ActiveTask.BeginTask("GetTraffic");
            var json = GetRawData();
            return GetTraffic(task, json);
        }


        /// <summary>
        /// Gets the list traffic from the last web page
        /// </summary>
        /// <returns>the list of traffic since the last call</returns>
        public List<NetworkTrafficData> GetTraffic(string json)
        {
            var task = _browser.ActiveTask.BeginTask("GetTraffic");
            return GetTraffic(task, json);
        }

        /// <summary>
        /// Gets the list traffic from the last web page
        /// </summary>
        /// <param name="task">The Task object</param>
        /// <param name="json">The json string</param>
        /// <returns>the list of traffic since the last call</returns>
        public static List<NetworkTrafficData> GetTraffic(Task task, string json)
        {
            var origJson = json;

            try
            {
				var result = DeserializeNetworkJson(json);
                task.Rollback();
                return result;
            }

            catch (Exception e)
            {
                WriteJsonToTask(task, origJson);

                task.Finish(e);
                throw;
            }
        }

        /// <summary>
        /// writes a bunch of JSON to the task's attributes
        /// </summary>
        /// <param name="task"></param>
        /// <param name="json"></param>
        public static void WriteJsonToTask(Task task, string json)
        {
            var counter = 1;
            var start = 0;
            while (start < json.Length)
            {
                var length = ((json.Length - start) > 7000)
                              ? 7000
                              : (json.Length - start);
                task.AddAttribute(string.Format("JSON{0}", counter.ToString("000")), json.Substring(start, length));
                start += length;
                counter++;
            }
        }

        /// <summary>
        /// Clears the logged traffic from selenium
        /// </summary>
        public void ClearTraffic()
        {
            GetRawData();
        }

        /// <summary>
        /// Stores the traffic into the logging system using the active task and current browser url
        /// </summary>
        public void LogTraffic()
        {
            LogTraffic(-1, _browser.Location, GetTraffic());
        }

        /// <summary>
        /// Stores the traffic into the logging system using the active task
        /// </summary>
        /// <param name="url">the current url</param>
        public void LogTraffic(string url)
        {
            LogTraffic(-1, url);
        }

        /// <summary>
        /// Stores the traffic log data into the logging system using the active task
        /// </summary>
        /// <param name="msUntilReady">milliseconds until the page was in ready state</param>
        /// <param name="url">the current url</param>
        public void LogTraffic(int msUntilReady, string url)
        {
            LogTraffic(msUntilReady, url, GetTraffic());
        }

        /// <summary>
        /// Stores the traffic into the logging system using the active task
        /// </summary>
        /// <param name="msUntilReady">milliseconds until the page was in ready state</param>
        /// <param name="url">the current url</param>
        /// <param name="data">the data to store</param>
        public void LogTraffic(int msUntilReady, string url, List<NetworkTrafficData> data)
        {
            var key = url.ToLower();
            int counter;

            if (!_urlCounters.TryGetValue(key, out counter))
                counter = 1;
            else
                counter++;

            _urlCounters[key] = counter;

            _browser.ActiveTask.StoreNetworkTraffic(url, _browser.SeleniumTestSession.RcHost.Host, counter, msUntilReady, data);
        }

        private string GetRawData()
        {
            return _browser.DoCommandNoLogging("captureNetworkTraffic", "json");
        }

		private static List<NetworkTrafficData> DeserializeNetworkJson(string json)
		{
			List<NetworkTrafficData> result = null;
			var lastjsone = string.Empty;

			for (; ; )
			{
				try
				{
					result = JsonConvert.DeserializeObject<List<NetworkTrafficData>>(json);
					break;
				}
				catch (JsonReaderException jsone)
				{
					if (!jsone.Message.Contains("escape sequence") || jsone.Message == lastjsone)
						throw;

					lastjsone = jsone.Message;

					// looking for: Bad JSON escape sequence: \:. Line 3511, position 29
					var lineno = new Regex(@"Line (\d+)").Match(jsone.Message);
					var position = new Regex(@"position (\d+)").Match(jsone.Message);

					if (lineno.Success && position.Success &&
						lineno.Groups.Count == 2 && position.Groups.Count == 2)
					{
						var line = int.Parse(lineno.Groups[1].Captures[0].Value);
						var pos = int.Parse(position.Groups[1].Captures[0].Value);

						using (var sr = new StringReader(json))
						{
							string linetext;
							var sb = new StringBuilder();
							var curline = 1;

							for (; ; )
							{
								linetext = sr.ReadLine();
								if (curline == line || linetext == null)
									break;
								sb.AppendLine(linetext);
								curline++;
							}

							if (linetext == null)
								throw; // give up

							while (pos > 0 && linetext[pos] != '\\')
								pos--;
							
							var newline = linetext.Insert(pos, "\\");
							sb.AppendLine(newline);

							sb.Append(sr.ReadToEnd());

							json = sb.ToString();

							sb.Clear();
						}
					}
				}
			}
			return result;
		}
    }
}
